/*
****************************************************************************************************************
*= = 版权信息：Copyright (C) 356 - 999. Ombres Industry Empiral. All rights reserved.
*= = 文件名称：Hubris.cxx
*= = 功能描述：风之谷数据库管理定义主文件
*= = 编写人员：Carlyon W. Tung
****************************************************************************************************************
*/

/*
*===============================================================================================================
*= = 声明文件
*===============================================================================================================
*/

#include <cstdio>
#include <fstream>
#include <iostream>
#include <string>

#include <libpq-fe.h>

#include <Ginkgo.h>
#include <Hyacinth.h>

#include "Hubris.h"

/*
*===============================================================================================================
*= = 预处理声明
*===============================================================================================================
*/

#ifdef HAVE_CONFIG_H
    #include <config.h>
#endif // End of HAVE_CONFIG_H

/*
*===============================================================================================================
*= = 宏定义
*===============================================================================================================
*/

/*
*===============================================================================================================
*= = 命名空间
*===============================================================================================================
*/

using namespace std;

/*
*===============================================================================================================
*= = 全局变量
*===============================================================================================================
*/

/*
*===============================================================================================================
*= = 类定义
*===============================================================================================================
*/

OHubris::OHubris()
{
    // TODO
}

OHubris::~OHubris()
{
    // TODO
}

OPostgreSQL::OPostgreSQL()
{
    m_psql_connect = nullptr;
    m_psql_result = nullptr;
}

OPostgreSQL::~OPostgreSQL()
{
    // TODO
}

void OPostgreSQL::SetHubrisXml(const std::string& HubrisXml)
{
    std::ifstream XmlFile(HubrisXml);

    if (!XmlFile.good())
    {
        m_HubrisXml = "..\\..\\Asgard\\Hubris.xml";
    }
    else
    {
        m_HubrisXml = HubrisXml;
    }

    std::ifstream XmlConfig(m_HubrisXml);

    if (!XmlConfig.good())
    {
        exit(1);
    }

    InitialPostgreSQLFromGinkgo();
}

void OPostgreSQL::InitialPostgreSQLFromGinkgo(void)
{
    m_ginkgo_postgres.set_config(m_HubrisXml);

    m_hostaddress = m_ginkgo_postgres.get_hostaddress().get();
    m_port = std::to_string(m_ginkgo_postgres.get_port());
    m_dbname = m_ginkgo_postgres.get_dbname().get();
    m_user = m_ginkgo_postgres.get_user().get();
    m_password = m_ginkgo_postgres.get_password().get();
    m_version = m_ginkgo_postgres.get_version().get();
}

int OPostgreSQL::ConnectDatabase(std::string& errmsg)
{
    std::string conn_sql;

    conn_sql = "hostaddr='" + m_hostaddress + "'";
    conn_sql += " ";
    conn_sql += "port=" + m_port;
    conn_sql += " ";
    conn_sql += "user=" + m_user;
    conn_sql += " ";
    conn_sql += "password=" + m_password;

    m_psql_connect = PQconnectdb(conn_sql.c_str());

    if (CONNECTION_OK != PQstatus(m_psql_connect))
    {
        errmsg = PQerrorMessage(m_psql_connect);
        return -1;
    }

#ifdef __INSIGHT_OS_WINDOWS__
    PQexec(m_psql_connect, "SET client_encoding = 'GBK'");
#else
    PQexec(m_psql_connect, "SET client_encoding = 'UTF-8'");
#endif

    return 0;
}

int OPostgreSQL::CreateDatabase(void)
{
    std::string query_sql;

    query_sql = "SELECT 1 FROM pg_database WHERE datname = '" + m_dbname + "';";
    m_psql_result = PQexec(m_psql_connect, query_sql.c_str());

    if (PGRES_TUPLES_OK == PQresultStatus(m_psql_result) && 0 < PQntuples(m_psql_result))
    {
        PQclear(m_psql_result);
    }
    else
    {
        std::string create_sql;

        create_sql = "CREATE DATABASE \"" + m_dbname + "\";";
        m_psql_result = PQexec(m_psql_connect, create_sql.c_str());

        if (PGRES_COMMAND_OK != PQresultStatus(m_psql_result))
        {
            PQclear(m_psql_result);
            return -1;
        }

        PQclear(m_psql_result);
    }

    return 0;
}

int OPostgreSQL::InsertMeasureItem(std::string& table, std::string& measure, double dmidv, std::string& variant)
{
    if (true != TableIsExists(table))
    {
        CreateTable(table);
        CreateSequence();
        CreateSuperTable(table);
    }

    std::string sql;

    sql = "INSERT INTO \"" + table + "\"";
    sql += " ";
    sql += "(time, measure, complex, variant) VALUES(NOW(), '";
    sql += measure + "',";
    sql += std::to_string(dmidv);
    sql += ", '";
    sql += variant + "');";

    PGresult* res = PQexec(m_psql_connect, sql.c_str());

    if (PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        PQclear(res);
        return false;
    }

    PQclear(res);
    return true;

    return 0;
}

int OPostgreSQL::SwitchDatabase(void)
{
    std::string conn_sql;

    conn_sql = "hostaddr='" + m_hostaddress + "'";
    conn_sql += " ";
    conn_sql += "port=" + m_port;
    conn_sql += " ";
    conn_sql += "dbname=" + m_dbname;
    conn_sql += " ";
    conn_sql += "user=" + m_user;
    conn_sql += " ";
    conn_sql += "password=" + m_password;

    m_psql_connect = PQconnectdb(conn_sql.c_str());

    if (CONNECTION_OK != PQstatus(m_psql_connect))
    {
        return -1;
    }

#ifdef __INSIGHT_OS_WINDOWS__
    PQexec(m_psql_connect, "SET client_encoding = 'GBK'");
#else
    PQexec(m_psql_connect, "SET client_encoding = 'UTF-8'");
#endif

    return 0;
}

int OPostgreSQL::InitialTimescaleDB(void)
{
    std::string query_sql;

    query_sql = "CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;";
    m_psql_result = PQexec(m_psql_connect, query_sql.c_str());

    if (PGRES_COMMAND_OK == PQresultStatus(m_psql_result) && 0 < PQntuples(m_psql_result))
    {
        PQclear(m_psql_result);
    }

    std::cout << PQerrorMessage(m_psql_connect) << std::endl;

    return 0;
}

bool OPostgreSQL::TableIsExists(const std::string& tableName)
{
    std::string query = "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = '" + tableName + "')";
    PGresult* res = PQexec(m_psql_connect, query.c_str());

    if (PQresultStatus(res) == PGRES_TUPLES_OK)
    {
        if (PQntuples(res) > 0)
        {
            char* exists = PQgetvalue(res, 0, 0);
            PQclear(res);
            return (exists[0] == 't');
        }
    }

    PQclear(res);
    return false;
}

bool OPostgreSQL::CreateTable(const std::string& tableName)
{
    std::string sql;

    sql = "CREATE TABLE \"" + tableName + "\"";
    sql += " ";
    sql += "(loongs INT PRIMARY KEY NOT NULL, time TIMESTAMPTZ NOT NULL, measure TEXT NOT NULL,";
    sql += " ";
    sql += "complex DOUBLE PRECISION NULL, variant TEXT NULL);";

    PGresult* res = PQexec(m_psql_connect, sql.c_str());

    if (PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        PQclear(res);
        return false;
    }

    PQclear(res);
    return true;
}

bool OPostgreSQL::CreateSuperTable(const std::string& tableName)
{
    std::string sql;

    sql = "SELECT create_hypertable ('" + tableName + "', 'loongs');";

    PGresult* res = PQexec(m_psql_connect, sql.c_str());

    if (PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        PQclear(res);
        return false;
    }

    PQclear(res);
    return true;
}

bool OPostgreSQL::CreateSequence(void)
{
    std::string sql;

    sql = "CREATE SEQUENCE first_sequence START 1;";

    PGresult* res = PQexec(m_psql_connect, sql.c_str());

    if (PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        PQclear(res);
        return false;
    }

    PQclear(res);

    sql = "ALTER TABLE orcinus ALTER COLUMN loongs SET DEFAULT nextval('first_sequence');";

    res = PQexec(m_psql_connect, sql.c_str());

    if (PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        PQclear(res);
        return false;
    }

    PQclear(res);
    return true;
}

int OPostgreSQL::CloseDatabase(void)
{
    PQfinish(m_psql_connect);

    return 0;
}

OTimescaleDB::OTimescaleDB()
{
    // TODO
}

OTimescaleDB::~OTimescaleDB()
{
    // TODO
}


/*
****************************************************************************************************************
*= = 文件结束
****************************************************************************************************************
*/


